package org.example.threadlocaltest.demos.configure;

import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import org.example.threadlocaltest.demos.eneity.domain.MyStudent;
import org.example.threadlocaltest.demos.eneity.service.MyStudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

@Component
//这是一个只会被调用一次的过滤器,注意，此处如果出现异常，全局异常处理器是捕获不到的，因为这个过滤器是在异常处理器（拦截器）之前执行的
public class MyOnceFilter extends OncePerRequestFilter {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

//    @Autowired
//    private LogEvent logEvent;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private MyStudentService myStudentService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        //记录一下日志--异步的方式来处理
        //方法一：使用 @Async 注解  这种方法需要注意，不能类里面调用类里面的方法，因为这样调用是没有办法实现异步的，
        // 需要将方法提取出来，在方法上加上 @Async 注解 并且在主启动类上加上 @EnableAsync 注解
//        logEvent.log("这是一个日志--@Async");

        //方法二：使用 CompletableFuture 这个方式就方便的多，不需要在主启动类上加注解，也不需要将方法提取出来，也可以指定线程池来执行
        MyStudent myStudent = new MyStudent();
        myStudent.setName("这是一个日志--CompletableFuture");
        CompletableFuture.runAsync(() -> myStudentService.saveLog(myStudent),threadPoolExecutor);


        //被拦截的请求会进入这里，从请求头中获取 token并解析
        String token = request.getHeader("token");
        //如果token为空，抛异常
        if (token == null) {
            //放行  后面还有过滤器会拦截的，发现是没有认证的请求，会拦截住的
            filterChain.doFilter(request, response);
            return;
        }

        //从redis中获取该token
        String json = stringRedisTemplate.opsForValue().get(token);
        Map<String, Object> authentication = new Gson().fromJson(json, Map.class);
        LinkedTreeMap<String, Object> principal = (LinkedTreeMap<String, Object>) authentication.get("principal");
        ArrayList<LinkedTreeMap<String, String>> authorities1 = (ArrayList) principal.get("authorities");


        ArrayList<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
        for (LinkedTreeMap<String, String> stringStringLinkedTreeMap : authorities1) {
            simpleGrantedAuthorities.add(new SimpleGrantedAuthority(stringStringLinkedTreeMap.get("role")));
        }

        MyUserDetails myUserDetails = new MyUserDetails((String) principal.get("username"), (String) principal.get("password"), simpleGrantedAuthorities);

        //存入 SecurityContextHolder 中
        SecurityContextHolder.getContext().setAuthentication(
                new UsernamePasswordAuthenticationToken(
                        myUserDetails, null, simpleGrantedAuthorities
                )

        );

        //放行
        filterChain.doFilter(request, response);


    }
}
